#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "llist.h"

struct node {
	void *data;
	struct node *prev;
	struct node *next;
};

struct header {
	int size;
	int way;   //thread safe or not
	int num;
	struct node node;
	pthread_mutex_t mut;
};

LLIST *llist_init(int way, int size)
{
	struct header *head;
	pthread_mutexattr_t mattr;

	if (THRD_SAFE == way)
	{
		pthread_mutexattr_init(&mattr);
		pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE_NP);
	}
	head = malloc(sizeof(struct header));
	if (head == NULL) {
		return NULL;
	}
	head->num = 0;
	head->way = way;
	head->size = size;
	head->node.data = NULL;
	head->node.prev = &head->node;
	head->node.next = &head->node;
	if (THRD_SAFE == way)
	{
		pthread_mutex_init(&head->mut, NULL);
		pthread_mutexattr_destroy(&mattr);	
	}

	return head; 	
}

int llist_number(LLIST *head)
{
	return ((struct header *)head)->num;
}

void llist_destroy(LLIST *head)
{
	struct node *cur, *next;
	struct header *ptr = head;

	if (NULL == ptr)
		return;
	if (THRD_SAFE == ptr->way)
		pthread_mutex_lock(&ptr->mut);
	for (cur = ptr->node.next; cur != &ptr->node; cur = next) {
		next = cur->next;
		free(cur->data);
		free(cur);
	}
	if (THRD_SAFE == ptr->way)
	{
		pthread_mutex_unlock(&ptr->mut);
		pthread_mutex_destroy(&ptr->mut);
	}
	free(ptr);
}

int llist_insert(LLIST *head, void *data, cmp_func_t *cmp, int mode)
{
	struct node *tmp, *cur, *next;
	struct header *ptr = head;

	if (NULL == ptr)
		goto Failed;
	tmp = malloc(sizeof(struct node));
	if (tmp == NULL)
		goto Failed;
	tmp->data = malloc(ptr->size);
	if (tmp->data == NULL)
	{
		free(tmp);
		goto Failed;
	}
	if (THRD_SAFE == ptr->way)
		pthread_mutex_lock(&ptr->mut);
	memcpy(tmp->data, data, ptr->size);
	if (&ptr->node == ptr->node.prev && &ptr->node == ptr->node.next)
	{
		tmp->next = &ptr->node;
		tmp->prev = &ptr->node;
		ptr->node.prev = tmp;
		ptr->node.next = tmp;
		goto Succ;
	}
	for (cur = ptr->node.next; cur != &ptr->node; cur = next) {
		next = cur->next;
		switch (mode)
		{
			case HEAD:
				tmp->next = ptr->node.next;
				tmp->prev = ptr->node.next->prev;
				ptr->node.next->prev = tmp;
				ptr->node.next = tmp;
				goto Succ;
			case TAIL:
				tmp->next = ptr->node.prev->next;
				tmp->prev = ptr->node.prev;
				ptr->node.prev->next = tmp;
				ptr->node.prev = tmp;
				goto Succ;
			case MINHEAD:
				if (cmp(data, cur->data) <= 0) {
					tmp->next = cur;
					tmp->prev = cur->prev;
					cur->prev->next = tmp;
					cur->prev = tmp;
					goto Succ;
				} else {
					if (&ptr->node == cur->next)
					{
						tmp->next = ptr->node.prev->next;
						tmp->prev = ptr->node.prev;
						ptr->node.prev->next = tmp;
						ptr->node.prev = tmp;
						goto Succ;
					}
				}
				break;
			case MAXHEAD:
				if (cur->next != &ptr->node &&
						cmp(data, cur->data) >= 0 &&
						cmp(data, cur->next->data) <= 0
				   ) {
					tmp->next = cur->next;
					tmp->prev = cur;
					cur->next->prev = tmp;
					cur->next = tmp;
					goto Succ;
				} else {
					if (&ptr->node == cur->next)
					{
						tmp->next = ptr->node.next;
						tmp->prev = ptr->node.next->prev;
						ptr->node.next->prev = tmp;
						ptr->node.next = tmp;
						goto Succ;
					}
				}
				break;
			default:
				goto Failed;
		}
	}

Failed:
	if (THRD_SAFE == ptr->way)
		pthread_mutex_unlock(&ptr->mut);
	return -1;
Succ:
	++ptr->num;
	//printf("LLIST NUM=[%d]\n", ptr->num);
	if (THRD_SAFE == ptr->way)
		pthread_mutex_unlock(&ptr->mut);
	return 0;
}

int llist_delete(LLIST *head, void *key, cmp_func_t *cmp)
{
	struct node *cur, *next;
	struct header *ptr = head;

	if (NULL == ptr)
		return -1;
	if (THRD_SAFE == ptr->way)
		pthread_mutex_lock(&ptr->mut);
	for (cur = ptr->node.next; cur != &ptr->node; cur = next) {
		next = cur->next;
		if (!cmp(key, cur->data)) {
			cur->prev->next = cur->next;
			cur->next->prev = cur->prev;
			free(cur->data);
			free(cur);
			--ptr->num;
			if (THRD_SAFE == ptr->way)
				pthread_mutex_unlock(&ptr->mut);
			return 0;
		}
	}
	if (THRD_SAFE == ptr->way)
		pthread_mutex_unlock(&ptr->mut);

	return -1;
}

void *llist_find__(LLIST *head, void *key, cmp_func_t *cmp)
{
	struct node *cur;
	struct header *ptr = head;

	if (NULL == ptr)
		return NULL;
	if (THRD_SAFE == ptr->way)
		pthread_mutex_lock(&ptr->mut);
	for (cur = ptr->node.next; cur != &ptr->node; cur = cur->next) {
		if (!cmp(key, cur->data)) {
			if (THRD_SAFE == ptr->way)
				pthread_mutex_unlock(&ptr->mut);
			return cur->data;
		}
	}
	if (THRD_SAFE == ptr->way)
		pthread_mutex_unlock(&ptr->mut);

	return NULL;
}

int llist_find(LLIST *head, void *key, cmp_func_t *cmp, void *ret)
{
	struct node *cur;
	struct header *ptr = head;

	if (NULL == ptr)
		return -1;
	if (THRD_SAFE == ptr->way)
		pthread_mutex_lock(&ptr->mut);
	for (cur = ptr->node.next; cur != &ptr->node; cur = cur->next) {
		if (!cmp(key, cur->data)) {
			memcpy(ret, cur->data, ptr->size);
			if (THRD_SAFE == ptr->way)
				pthread_mutex_unlock(&ptr->mut);
			return 0;
		}
	}
	if (THRD_SAFE == ptr->way)
		pthread_mutex_unlock(&ptr->mut);

	return -1;
}

void llist_travel(LLIST *head, travel_func_t *travel)
{
	struct node *cur;
	struct header *ptr = head;
	pthread_mutexattr_t mattr;

	if (NULL == ptr)
		return;
	/* Note here: recursive mutex lock */
	if (THRD_SAFE == ptr->way)
	{
		pthread_mutexattr_init(&mattr);
		pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE_NP);
		pthread_mutex_lock(&ptr->mut);
	}
	for (cur = ptr->node.next; cur != &ptr->node; cur = cur->next) {
		travel(cur->data);
	}
	if (THRD_SAFE == ptr->way)
	{
		pthread_mutex_unlock(&ptr->mut);
		pthread_mutexattr_destroy(&mattr);
	}
}

int llist_sort(LLIST *head, cmp_func_t *cmp, int mode)
{
	void *prevnode, *nextnode;
	struct node *cur, *next;
	struct header *ptr = head;

	if (NULL == ptr)
		return -1;
	if (THRD_SAFE == ptr->way)
		pthread_mutex_lock(&ptr->mut);
	for (cur = ptr->node.next; cur != &ptr->node; cur = cur->next) {
		for (next = cur->next; next != &ptr->node; next = next->next) {
			if (mode == MINHEAD) {
				if (cmp(cur->data, next->data) <= 0) {
					continue;
				}
			} else if (mode == MAXHEAD) {
				if (cmp(cur->data, next->data) >= 0) {
					continue;
				}
			}
			prevnode = next->prev;
			nextnode = next->next;
			next->prev->next = next->next;
			next->next->prev = next->prev;
			next->prev = cur->prev;
			next->next = cur->next;
			cur->prev->next = next;
			cur->next->prev = next;
			cur->prev = prevnode;
			cur->next = nextnode;
			cur->prev->next = cur;
			cur->next->prev = cur;
		}
	}
	if (THRD_SAFE == ptr->way)
		pthread_mutex_unlock(&ptr->mut);

	return 0;
}

int llist_fetch(LLIST *head, void *key, cmp_func_t *cmp, void *ret)
{
	struct node *cur, *next;
	struct header *ptr = head;

	if (NULL == ptr)
		return -1;
	if (THRD_SAFE == ptr->way)
		pthread_mutex_lock(&ptr->mut);
	for (cur = ptr->node.next; cur != &ptr->node; cur = next) {
		next = cur->next;
		if (!cmp(key, cur->data)) {
			memcpy(ret, cur->data, ptr->size);  
			cur->prev->next = cur->next;
			cur->next->prev = cur->prev;
			free(cur->data);
			free(cur);
			--ptr->num;
			if (THRD_SAFE == ptr->way)
				pthread_mutex_unlock(&ptr->mut);
			return 0;
		}
	}
	if (THRD_SAFE == ptr->way)
		pthread_mutex_unlock(&ptr->mut);

	return -1; 
}

int llist_fetch_head(LLIST *head, void *data)
{
	struct header *ptr = head;

	if (NULL == ptr)
		return -1;
	if (THRD_SAFE == ptr->way)
		pthread_mutex_lock(&ptr->mut);
	if (ptr->node.next != &ptr->node) {
		memcpy(data, ptr->node.next->data, ptr->size);	
		if (THRD_SAFE == ptr->way)
			pthread_mutex_unlock(&ptr->mut);
		return 0;	
	}
	if (THRD_SAFE == ptr->way)
		pthread_mutex_unlock(&ptr->mut);

	return -1;
}

int llist_modify_node(LLIST *head, void *key, cmp_func_t *cmp, void *modify)
{
	struct node *cur;
	struct header *ptr = head;

	if (NULL == ptr)
		return -1;
	if (THRD_SAFE == ptr->way)
		pthread_mutex_lock(&ptr->mut);
	for (cur = ptr->node.next; cur != &ptr->node; cur = cur->next) {
		if (!cmp(key, cur->data)) {
			memcpy(cur->data, modify, ptr->size);  
			if (THRD_SAFE == ptr->way)
				pthread_mutex_unlock(&ptr->mut);
			return 0;
		}
	}
	if (THRD_SAFE == ptr->way)
		pthread_mutex_unlock(&ptr->mut);

	return -1;	
}

static int cmp_del(void *key, void *data)
{
	return 0;
}

int llist_empty(LLIST *head)
{
	struct node *cur;
	struct header *ptr = head;
	pthread_mutexattr_t mattr;

	if (NULL == ptr)
		return -1;
	/* Note here: recursive mutex lock */
	if (THRD_SAFE == ptr->way)
	{
		pthread_mutexattr_init(&mattr);
		pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE_NP);
		pthread_mutex_lock(&ptr->mut);
	}
	for (cur = ptr->node.next; cur != &ptr->node; cur = cur->next) {
		if (llist_delete(ptr, cur->data, cmp_del) < 0) {
			if (THRD_SAFE == ptr->way)
			{
				pthread_mutex_unlock(&ptr->mut);
				pthread_mutexattr_destroy(&mattr);
			}
			return -1; 
		}
	}
	if (THRD_SAFE == ptr->way)
	{
		pthread_mutex_unlock(&ptr->mut);
		pthread_mutexattr_destroy(&mattr);
	}

	return 0;
}

int llist_isempty(LLIST *head)
{
	struct header *ptr = head;

	return (NULL != ptr && &ptr->node == ptr->node.next);
}

